package org.ring.common.utils.reflect;

import java.lang.reflect.Method;

/**
 * Jvm规范 类型标识
 * boolean: Z
 * byte: B
 * char: C
 * short: S
 * int: I
 * long: J
 * float: F
 * double: D
 * <p>
 * 对于数组类型，其内部表示为 [ 加上元素类型的内部名称。例如，一个整数数组表示为 [I
 * <p>
 * 对于对象类型（即引用类型），其内部表示为 / 分隔的包名和类名，或者更常见的是，
 * 直接使用点分隔的全限定类名（但不是在字节码文件中）。
 * 例如，java.lang.String 在某些上下文中会表示为 Ljava/lang/String;。这里的 L 表示类或接口的开始，; 表示结束
 * <p>
 * 此外，对于方法类型，其内部表示包括返回类型和参数类型的序列。
 * 例如，一个接受一个整数并返回一个整数的方法可以表示为 I(I)。
 * 如果方法有多个参数，则按照它们出现的顺序列出，
 * 例如 (II)I 表示一个接受两个整数并返回一个整数的方法
 *
 * @since 2024-10-28 09:10
 */
public final class MethodDescriptor {

    /**
     * JVM descriptor
     */
    private final static char ARRAY_DP = '[';

    /**
     * void(V)
     */
    private final static char VOID = 'V';

    private final static char INT = 'I';

    private final static char SHORT = 'S';

    private final static char FLOAT = 'F';

    private final static char DOUBLE = 'D';

    private final static char LONG = 'L';

    private final static char CHAR = 'C';

    private final static char BYTE = 'B';

    private final static char BOOL = 'Z';

    private final Method method;

    private final Class<?>[] parameterTypes;
    private final String parameterDescriptor;

    public MethodDescriptor(Method method) {
        this.method = method;
        this.parameterTypes = method.getParameterTypes();
        this.parameterDescriptor = getDescriptor(this.parameterTypes);
    }

    public static String getDescriptor(final Class<?>[] parameterTypes) {
        if (parameterTypes == null || parameterTypes.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder(128);
        for (Class<?> cls : parameterTypes) {
            sb.append(getDescriptor(cls));
        }
        return sb.toString();

    }

    /**
     * @return
     */
    public static String getDescriptor(Class<?> c) {
        if(c.isPrimitive()){
            return String.valueOf(getPrimitiveSimpleName(c));
        }
        String name = c.getName();
        if (c.isArray()) {
            return name.replace('.','/');
        }
        return "L" + name.replace('.','/') + ";";
    }

    private static char getPrimitiveSimpleName(final Class c) {

        if (Integer.TYPE.equals(c)) {
            return INT;
        } else if (Short.TYPE.equals(c)) {
            return SHORT;
        } else if (Long.TYPE.equals(c)) {
            return LONG;
        } else if (Double.TYPE.equals(c)) {
            return DOUBLE;
        } else if (Float.TYPE.equals(c)) {
            return FLOAT;
        } else if (Byte.TYPE.equals(c)) {
            return BYTE;
        } else if (Character.TYPE.equals(c)) {
            return CHAR;
        } else if (Boolean.TYPE.equals(c)) {
            return BOOL;
        } else if (Void.TYPE.equals(c)) {
            return VOID;
        } else {
            throw new IllegalStateException("The class " + c.getName() + "is not Primitive");
        }
    }

    public Method getMethod() {
        return method;
    }

    public String getParameterDescriptor() {
        return this.parameterDescriptor;
    }

    public Class<?>[] getParameterTypes() {
        return parameterTypes;
    }
}
